#include <iostream>
#include <errno.h>
#include "btSocket.h"
#include "btNetDefine.h"
#include "btNetStruct.h"
#include "myconvert.h"
#include "btNetHandler.h"
#include "cocos2d.h"
#ifdef WIN32
#include "win32/FOLog.h"
#endif
USING_NS_CC;
#include <signal.h>

#ifdef WIN32
	#pragma comment(lib, "wsock32")
#endif

// 默认处理sigpipe函数 [2013/8/29 17:36 butcher]
#ifndef WIN32
void handle_pipe(int sig)
{
    CCLOG("catch sigpipe");
}
#endif

CSocket::CSocket(SOCKET sock)
{
	m_sock = sock;
	//设定sigpipe处理函数,不再由系统默认处理
#ifndef WIN32
    //signal( SIGPIPE, SIG_IGN );
    struct sigaction action;
    action.sa_handler = SIG_IGN;;
    sigaction(SIGPIPE,&action,NULL);
    int nNetTimeout = 1000;
    setsockopt( m_sock, SOL_SOCKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
#endif
}

CSocket::~CSocket()
{
}

int CSocket::Init()
{
#ifdef WIN32
	/*
     http://msdn.microsoft.com/zh-cn/vstudio/ms741563(en-us,VS.85).aspx
     
     typedef struct WSAData {
     WORD wVersion;								//winsock version
     WORD wHighVersion;							//The highest version of the Windows Sockets specification that the Ws2_32.dll can support
     char szDescription[WSADESCRIPTION_LEN+1];
     char szSystemStatus[WSASYSSTATUS_LEN+1];
     unsigned short iMaxSockets;
     unsigned short iMaxUdpDg;
     char FAR * lpVendorInfo;
     }WSADATA, *LPWSADATA;
     */
	WSADATA wsaData;
	//#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
	WORD version = MAKEWORD(2, 0);
	int ret = WSAStartup(version, &wsaData);//win sock start up
	if ( ret ) {
        //		cerr << "Initilize winsock error !" << endl;
		return -1;
	}
#endif
	
	return 0;
}
//this is just for windows
int CSocket::Clean()
{
#ifdef WIN32
    return (WSACleanup());
#endif
    return 0;
}

CSocket& CSocket::operator = (SOCKET s)
{
	m_sock = s;
	return (*this);
}
CSocket::operator SOCKET ()
{
	return m_sock;
}
//create a socket object win/lin is the same
// af:
bool CSocket::Create(int af, int type, int protocol)
{
	m_sock = socket(af, type, protocol);
	if ( m_sock == INVALID_SOCKET ) {
		CCLOG("CSocket create socket error! ecode:%d",GetError());
		return false;
	}
	CCLOG("CSocket create succ");
	return true;
}
/******************************************************************
 //funcName:Connect
 
 //date:2013/3/15 11:21
 
 //desc:连接服务器,尝试5次左右
 
 //param:
 
 //ret value:
 连接的代码
 
 //author:butcher
 ******************************************************************/
int CSocket::Connect(const char* ip, unsigned short port,int& ecode)
{
	struct sockaddr_in svraddr;
	memset(&svraddr, 0, sizeof(svraddr));
	svraddr.sin_family = AF_INET;
	svraddr.sin_addr.s_addr = inet_addr(ip);
	svraddr.sin_port = htons(port);
	//尝试5次
	int count = 0;
	int ret = 0;
	do{
		ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
		if ( ret != 0 ) {
			continue;
		}
		break;
	}while(count++ < 5);//应该在这里尝试多次
	if(ret != 0){
		ecode = GetError();
		CCLOG("CSocket connect error! ecode:%d",ecode);
	}else{
		CCLOG("CSocket connect succ");
	}
	return ret;
}

bool CSocket::Bind(unsigned short port)
{
	struct sockaddr_in svraddr;
	svraddr.sin_family = AF_INET;
	svraddr.sin_addr.s_addr = INADDR_ANY;
	svraddr.sin_port = htons(port);
    
	int opt =  1;
	if ( setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0 )
		return false;
    
	int ret = bind(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
	if ( ret == SOCKET_ERROR ) {
		return false;
	}
	return true;
}
//for server
bool CSocket::Listen(int backlog)
{
	int ret = listen(m_sock, backlog);
	if ( ret == SOCKET_ERROR ) {
		return false;
	}
	return true;
}

bool CSocket::Accept(CSocket& s, char* fromip)
{
	struct sockaddr_in cliaddr;
	socklen_t addrlen = sizeof(cliaddr);
	SOCKET sock = accept(m_sock, (struct sockaddr*)&cliaddr, &addrlen);
	if ( sock == SOCKET_ERROR ) {
		return false;
	}
    
	s = sock;
	if ( fromip != NULL )
		sprintf(fromip, "%s", inet_ntoa(cliaddr.sin_addr));
    
	return true;
}//end for server
/******************************************************************
 //funcName:Send
 
 //date:2013/3/13 18:32
 
 //desc:发送数据
 
 //param:
 buf:发送的数据
 len:发送的长度
 flags:一般为0
 
 //ret value:
 -1:发送数据的时候断开,或者发送数据错误了,都返回error
 >0:实际发送的数据长度
 
 //author:butcher
 ******************************************************************/
int CSocket::Send(const char* buf, int len, int flags,int& errorcode)
{
	int bytes = 0;
	int count = 0;
    
	while ( count < len ) {
		bytes = send(m_sock, buf + count, len - count, flags);
		if(bytes <= 0){
			errorcode = GetError();
			CNetHandler::sharedNetHandler()->onNetError(bytes,errorcode);
#ifdef USE_LOG
			CFOLog::getSingletonPtr()->addString("CSocket::Send error! retvalue:%d,errorcode:%d",bytes,errorcode);
#endif
			return bytes;
		}
		count += bytes;
	}
	return count;
}
/******************************************************************
 //funcName:Recv
 
 //date:2013/3/13 18:31
 
 //desc:接收数据
 根据服务器反馈的数据,先读取长度,然后根据长度再读取需要的内容
 //param:
 buf:自定义的缓冲区
 len:缓冲区的长度
 flags:一般是0
 errcode:错误之后的 错误的返回码
 //ret value:
 0:服务器关闭
 -1:接受的时候出错,通过GetError()获取
 >0:正常返回
 
 //author:butcher
 ******************************************************************/
int CSocket::Recv(char** buf, int flags,int& errcode)
{
	int bytesRecv = 0;
	int totalrecvlen = 0;
	char lenbuf[10] = {0};
	//读取长度信息
	if(m_sock == 0)
		return 0;
    //*********************************************************start/
    //先用这个替代下，找到更好的办法以后再说
//    struct timeval timeout;
//    timeout.tv_sec = 10;
//    timeout.tv_usec = 0;
//    fd_set readsetfd;
//    int maxfd = 0;
//    int ret;
//    FD_ZERO (&readsetfd);
//    FD_SET (m_sock, &readsetfd);
//    if (maxfd<m_sock) {
//        maxfd = m_sock;
//    }
//    
//    ret = select(maxfd + 1, &readsetfd, NULL, NULL, &timeout);
//    if (ret<=0) {
//        errcode = GetError();
//#ifdef USE_LOG
//        CFOLog::getSingletonPtr()->addString("CSocket::Recv0 error! retvalue:%d,errorcode:%d",bytesRecv,errcode);
//#endif
//        return ret;
//    }
//    struct timeval tv;
//    tv.tv_sec = 1000;  //阻塞时间5秒
//    
//    setsockopt(m_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));
    //********************************************************end/
	bytesRecv = recv(m_sock, lenbuf, SERVER_MSG_HEADER_LENGTH, flags);
	if(bytesRecv > 0){
		int toReadLen = (((unsigned char)lenbuf[0]) << 24) +
        (((unsigned char)lenbuf[1]) << 16) +
        (((unsigned char)lenbuf[2]) << 8) +
        (((unsigned char)lenbuf[3]));
		char* allocBuf = new char[toReadLen + 1];
		memset(allocBuf,0,toReadLen + 1);
		*buf = allocBuf;
		toReadLen -= SERVER_MSG_HEADER_LENGTH;
		totalrecvlen = toReadLen;
		while(toReadLen > 0){
			if(m_sock == 0)
				return 0;
			bytesRecv = recv(m_sock, allocBuf, toReadLen, flags);
			if(bytesRecv > 0){
				toReadLen -= bytesRecv;
				allocBuf += bytesRecv;
			}else{
				errcode = GetError();
#ifdef USE_LOG
				CFOLog::getSingletonPtr()->addString("CSocket::Recv0 error! retvalue:%d,errorcode:%d",bytesRecv,errcode);
#endif
				return bytesRecv;
			}
		}
	}else{
		CCLOG("recv error! closed!");
		errcode = GetError();
#ifdef USE_LOG
		CFOLog::getSingletonPtr()->addString("CSocket::Recv1 error! retvalue:%d,errorcode:%d",bytesRecv,errcode);
#endif
		return bytesRecv;
	}
	return totalrecvlen;
}
/******************************************************************
 //funcName:onSocketClose
 
 //date:2013/3/16 22:32
 
 //desc:当发现服务器连接失败的时候,做的处理
 
 //param:
 
 //ret value:
 
 //author:butcher
 ******************************************************************/
void CSocket::onSocketClose()
{
	CCLOG("onSocketClose");
	CNetHandler::sharedNetHandler()->ReleaseSocket();
}
void CSocket::onSocketSendError()
{
    
}
int CSocket::Close()
{
	CCLOG("CSocket close");
#ifdef WIN32
	return (closesocket(m_sock));
#else
    if (m_sock) {
        return NULL;
    }
	return (close(m_sock));
#endif
}

int CSocket::GetError()
{
#ifdef WIN32
	return (WSAGetLastError());
#else
	return (errno);
#endif
}
/******************************************************************
 //funcName:IsCoutinueError
 
 //date:2013/3/14 19:53
 
 //desc:收到返回值 SOCKET_ERROR的时候,
 如果getError是如下几个值,那么继续等待,否则就结束
 not error but exception can continue
 //param:
 
 //ret value:
 
 //author:butcher
 ******************************************************************/
bool CSocket::IsCoutinueError(int errorNr)
{
	if(errorNr == EINTR //
       ||errorNr == EAGAIN //may be exceed max send buffer
       ||errorNr == EWOULDBLOCK
       )
		return true;
	return false;
}

bool CSocket::DnsParse(const char* domain, char* ip)
{
	struct hostent* p;
	if ( (p = gethostbyname(domain)) == NULL )
		return false;
    
	sprintf(ip,
            "%u.%u.%u.%u",
            (unsigned char)p->h_addr_list[0][0],
            (unsigned char)p->h_addr_list[0][1], 
            (unsigned char)p->h_addr_list[0][2], 
            (unsigned char)p->h_addr_list[0][3]);
	
	return true;
}
